package cn.ad.server.web.spring.service.actions;

import cn.ad.server.common.utils.monitor.MonitorHandler;
import cn.ad.server.persistence.ConfigRepository;
import cn.ad.server.web.exception.AdServerMessageException;
import cn.ad.server.web.filter.MatcherChain;
import cn.ad.server.web.log.IAdServerLogger;
import cn.ad.server.web.service.AdService;
import cn.ad.server.web.service.IAdspaceService;
import cn.ad.server.web.spring.model.AdRequest;
import cn.ad.server.web.spring.model.AdResponse;
import cn.ad.server.web.spring.model.sts.STSAdRequest;
import cn.ad.server.web.spring.model.sts.STSAdResponse;
import cn.ad.server.web.spring.service.actions.command.AdRequestCommand;
import cn.ad.server.web.spring.service.actions.context.AdContext;
import cn.ad.server.web.spring.service.actions.handler.AdFormatHandler;
import cn.ad.server.web.spring.service.actions.handler.DefaultAdFormatHandler;
import cn.ad.server.web.spring.service.actions.handler.OriginAdFormatHandler;
import cn.ad.server.web.spring.service.actions.handler.VideoAdFormatHandler;
import cn.ad.server.web.spring.service.actions.handler.gdt.server.GDTMobileServerHandler;
import cn.ad.server.web.spring.service.actions.targeturl.ConvertTargetUrlHandler;
import cn.ad.server.web.spring.service.actions.targeturl.IConvertTargetUrl;
import cn.ad.server.web.spring.service.biz.TrackingService;
import cn.ad.server.web.spring.service.components.adtraffic.AdTrafficDispatcherManager;
import cn.ad.server.web.spring.service.components.adtraffic.TrafficRequest;
import cn.ad.server.web.spring.service.components.adtraffic.TrafficResponse;
import cn.ad.server.domain.ad.*;
import cn.ad.server.domain.enumration.AdFormat;
import cn.ad.server.domain.enumration.ReturnCode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.*;

/**
 * Server 2 Server方式广告请求处理服务
 * 
 * @author Wu Chengjun
 */
@Component("action-sts")
public class STSAdRequestAction implements IAdRequestAction {

	private static final Logger LOGGER = LoggerFactory.getLogger(STSAdRequestAction.class);

	@Autowired
	private ConfigRepository configRepository;

	/**
	 * 点击监测附加宏
	 */
	private static final String CLICK_DEFINITION = "&lon=%%LON%%&lat=%%LAT%%&downx=%%DOWNX%%&downy=%%DOWNY%%&upx=%%UPX%%&upy=%%UPY%%";

	/**
	 * 展示监测附加宏
	 */
	private static final String IMPRESSION_DEFINITION = "&lon=%%LON%%&lat=%%LAT%%";

	@Autowired
	private AdService adService;

	@Autowired
	private IAdspaceService adspaceService;

	@Autowired
	private TrackingService trackingService;

	@Autowired
	private GDTMobileServerHandler gdtMobileServerHandler;

	@Autowired
	private IAdServerLogger loggerService;

	@Resource(name = "targetMatcherChain")
	private MatcherChain matcherChain;

	private boolean debugMode;

	@Resource
	private String serverAddress;

	@Resource(name = "targetUrlConvert")
	public Map<String, IConvertTargetUrl> targetUrlConvert;

	@Autowired
	private ConvertTargetUrlHandler convertTargetUrlHandler;

	@Autowired
	private AdTrafficDispatcherManager trafficDispatcherManager;

	// @Value("${bid.adView.ratio}")
	// private double ratio;

	private static final DecimalFormat df = new DecimalFormat("###0.00");

	private static HashMap<AdFormat, AdFormatHandler> adFormatHandlers = new HashMap<>();

	static {

		adFormatHandlers.put(AdFormat.BANNER, new DefaultAdFormatHandler());
		adFormatHandlers.put(AdFormat.TABLE_SCREEN, new DefaultAdFormatHandler());
		adFormatHandlers.put(AdFormat.OPEN_SCREEN, new DefaultAdFormatHandler());
		adFormatHandlers.put(AdFormat.VIDEO, new VideoAdFormatHandler());
		adFormatHandlers.put(AdFormat.AD_NATIVE, new OriginAdFormatHandler());
		df.setRoundingMode(RoundingMode.HALF_UP);

	}

	@Override
	public AdResponse doRequest(AdRequest request) throws RequestActionException {
		// 执行选择默认创意的流程
		STSAdResponse adResponse = new STSAdResponse();
		if (request instanceof STSAdRequest) {
			STSAdRequest req = (STSAdRequest) request;
			MonitorHandler.monitorAdSpace(configRepository.fetchMonitoredAdSpace(), configRepository.fetchDebugMode(),
					AdSpace.class.getName(),
					String.valueOf(req.getImpression().length > 0 ? req.getImpression()[0].getAid() : 0),
					"---------monitor start----------");
			// 设置一些前置返回
			adResponse.setVersion(req.getVersion());
			adResponse.setExtendData(req.getExtendData());
			try {
				process(req, adResponse);
			} catch (AdServerMessageException e) {
				adResponse.setStatus(e.getReturnCode().getCode());
				adResponse.setMessage(e.getMessage());
			} catch (DataAccessException ex) {
				throw new RequestActionException(ex.getMessage(), ex);
			} catch (Exception e) {
				adResponse.setStatus(ReturnCode.E1.getCode());
				adResponse.setMessage(ReturnCode.E1.getMessage());
				LOGGER.error("Server to server 广告请求处理出现异常，原因:{}", e.getMessage(), e);
			} finally {
				MonitorHandler.monitorAdSpace(configRepository.fetchMonitoredAdSpace(),
						configRepository.fetchDebugMode(), AdSpace.class.getName(),
						String.valueOf(req.getImpression().length > 0 ? req.getImpression()[0].getAid() : 0),
						"---------monitor end----------");
				adService.unbond();
			}
			return adResponse;
		} else {
			throw new RequestActionException("The param[request] is not supported, because it not a instance of "
					+ STSAdResponse.class.getName() + ", it's type is " + request.getClass().getName());
		}

	}

	private void process(STSAdRequest adRequest, STSAdResponse adResponse) {
		// 从redis取adSpace数据 key从redis里面获取
		STSAdRequest.Impression[] impressions = adRequest.getImpression();

		debugMode = adRequest.isDebugMode();

		// set AdResponseData
		STSAdResponse.AdResponseData data = new STSAdResponse.AdResponseData();
		adResponse.setData(data);

		STSAdResponse.AdSpace[] adSpaces = new STSAdResponse.AdSpace[impressions.length];

		for (int i = 0; i < impressions.length; i++) {

			STSAdRequest.Impression impression = impressions[i];

			// 获取adContext
			AdContext adContext = (AdContext) adRequest.getAdContext().clone();
			adContext.setPageSize(impression.getPageSize());

			try {
				adSpaces[i] = doProcess(adRequest, impression, adContext);
			} catch (Exception e) {
				throw e;
			} finally {
				loggerService.logAdRequest(adContext);
			}

		}

		data.setAdSpace(adSpaces);
		adResponse.setStatus(ReturnCode.S0.getCode());
		adResponse.setMessage(ReturnCode.S0.getMessage());
	}

	/**
	 * 分发流量到其他渠道
	 * 
	 * @param adContext
	 * @param adSpace
	 * @param localAdSpace
	 * @param returnCode
	 * @return
	 */
	private STSAdResponse.AdSpace dispatchTraffic(AdContext adContext, STSAdResponse.AdSpace adSpace,
			AdSpace localAdSpace, ReturnCode returnCode) throws Exception {
		// 为了匹配之前的参数模式，暂时采用扩展参数的形式兼容之前的请求和响应参数
		TrafficRequest trafficRequest = new TrafficRequest();
		trafficRequest.addExParam("AdContext", adContext);
		trafficRequest.addExParam("STSAdResponse.AdSpace", adSpace);
		trafficRequest.addExParam("AdSpace", localAdSpace);
		trafficRequest.addExParam("ReturnCode", returnCode);

		TrafficResponse trafficResponse = trafficDispatcherManager.dispatchTraffic(trafficRequest);
		if (trafficResponse != null) {
			return (STSAdResponse.AdSpace) trafficResponse.getExParam("STSAdResponse.AdSpace");
		}
		return null;
	}

	private STSAdResponse.AdSpace doProcess(STSAdRequest adRequest, STSAdRequest.Impression impression,
			AdContext adContext) {

		// 填充adContext的业务信息
		adContext.setAdSpaceId(impression.getAid());

		// 获取广告位信息
		AdRequestCommand adRequestCommand = new AdRequestCommand();
		adRequestCommand.setAdSpaceId(impression.getAid());
		adRequestCommand.setaWidth(impression.getWidth());
		adRequestCommand.setaHeight(impression.getHeight());

		adRequestCommand.setScreenWidth(adContext.getScreenWidth());
		adRequestCommand.setScreenHeight(adContext.getScreenHeight());

		// HIS:历史代码 AdSpace localAdSpace =
		// adService.fetchAdSpace(adRequestCommand);
		// AreaPlatformMap fetchAreaPlatformMapByTargetId =
		// adService.fetchAreaPlatformMapByTargetId(130100);
		AdSpace localAdSpace = adspaceService.findAdSpaceByID(adRequestCommand.getAdSpaceId());

		// 生成广告位信息
		STSAdResponse.AdSpace adSpace = new STSAdResponse.AdSpace();

		adContext.setAdFormat(localAdSpace.getAdFormat().getValue());
		adContext.setAdSpaceWidth(impression.getWidth());
		adContext.setAdSpaceHeight(impression.getHeight());
		adSpace.setAid(localAdSpace.getId());
		adSpace.setAdFormat(localAdSpace.getAdFormat().getValue());
		Ratio ratio = localAdSpace.getRatio();
		if (ratio != null) {
			adContext.setAdSpaceRatio(ratio.getValue());
		}

		// 匹配策略
		matchStrategy(localAdSpace, adContext);

		if (localAdSpace.getMatchStrategy() == null) {
			// 当流量没有匹配的策略时，将流量分发到其他渠道.
			try {
				return dispatchTraffic(adContext, adSpace, localAdSpace, ReturnCode.E20004);
				// return gdtMobileServerHandler.request(adContext, adSpace,
				// localAdSpace, ReturnCode.E20004);
			} catch (Exception e) {
				if (localAdSpace.getDefaultCreative() == null) {
					throw new AdServerMessageException(ReturnCode.E20004);
				} else {
					localAdSpace.setMatchCreative(localAdSpace.getDefaultCreative());
				}
			}
		} else {
			adContext.setStrategyId(localAdSpace.getMatchStrategy().getId());
			adContext.setCampaignId(localAdSpace.getMatchStrategy().getCampaign().getId());
		}

		switch (AdFormat.adFormatMap.get(localAdSpace.getAdFormat().getValue())) {
			case BANNER:
			case TABLE_SCREEN:
			case OPEN_SCREEN:

				AdFormatHandler adFormatHandler = adFormatHandlers.get(AdFormat.OPEN_SCREEN);
				adFormatHandler.matchCreative(localAdSpace);
				if (localAdSpace.getMatchCreative() == null) {
					try {
						return dispatchTraffic(adContext, adSpace, localAdSpace, ReturnCode.E20003);
					} catch (Exception e) {
						throw new AdServerMessageException(ReturnCode.E20003);
					}
				}

				// 匹配素材
				matchMaterial(localAdSpace, adRequestCommand);
				if (localAdSpace.getMatchAdMaterial() == null) {
					try {
						return dispatchTraffic(adContext, adSpace, localAdSpace, ReturnCode.E20005);
					} catch (Exception e) {
						throw new AdServerMessageException(ReturnCode.E20005);
					}
				}

				adFormatHandler.compositeMaterial(localAdSpace, adSpace, adContext.getOs());

				break;
			case VIDEO:

				adFormatHandler = adFormatHandlers.get(AdFormat.VIDEO);
				adFormatHandler.matchCreative(localAdSpace);
				if (localAdSpace.getMatchCreative() == null) {
					try {
						return dispatchTraffic(adContext, adSpace, localAdSpace, ReturnCode.E20003);
					} catch (Exception e) {
						throw new AdServerMessageException(ReturnCode.E20003);
					}
				}
				// 匹配素材
				matchMaterial(localAdSpace, adRequestCommand);
				if (localAdSpace.getMatchAdMaterial() == null) {
					try {
						return dispatchTraffic(adContext, adSpace, localAdSpace, ReturnCode.E20005);
					} catch (Exception e) {
						throw new AdServerMessageException(ReturnCode.E20005);
					}
				}
				adFormatHandler.compositeMaterial(localAdSpace, adSpace, adContext.getOs());
				break;
			case AD_NATIVE:

				adFormatHandler = adFormatHandlers.get(AdFormat.AD_NATIVE);
				adFormatHandler.matchCreative(localAdSpace);
				if (localAdSpace.getMatchCreative() == null) {
					try {
						return dispatchTraffic(adContext, adSpace, localAdSpace, ReturnCode.E20003);
					} catch (Exception e) {
						throw new AdServerMessageException(ReturnCode.E20003);
					}
				}
				adFormatHandler.compositeMaterial(localAdSpace, adSpace, adContext.getOs());
				// 设置templateId
				adContext.setTemplateId(localAdSpace.getAdTemplate().getId());
				break;
			default:
				break;

		}

		// 设置creativeId,materialId,maxDuration(播放时长)
		adContext.setCreativeId(localAdSpace.getMatchCreative().getId());

		if (localAdSpace.getMatchAdMaterial() != null) {
			adContext.setMaterialId(localAdSpace.getMatchAdMaterial().getId());
			adContext.setMaxDuration(localAdSpace.getMatchAdMaterial().getDuration());
		}

		generateGuid(adContext, adRequest.getDevice(), localAdSpace, localAdSpace.getMatchStrategy());

		/**
		 * 拼写展示和点击url
		 */
		// String impUrlFormat = serverAddress +
		// "imp.html?version=%s&aid=%s&sid=%s&cid=%s&mid=%s&guid=%s";
		// String impUrl = String.format(impUrlFormat, adRequest.getVersion(),
		// adContext.getAdSpaceId(),
		// adContext.getStrategyId(), adContext.getCreativeId(),
		// adContext.getMaterialId(), adContext.getGuid())
		// + "&lon=%%LON%%&lat=%%LAT%%";

		String impUrl = trackingService.buildImpressionUrl(adRequest.getVersion() + "",
				NumberUtils.toInt(adRequest.getChannelID(), 1), 1, adContext.getAdSpaceId(), adContext.getStrategyId(),
				adContext.getCreativeId(), adContext.getMaterialId(), adContext.getGuid(), adContext.getOs());
		impUrl += IMPRESSION_DEFINITION;
		STSAdResponse.Creative creative = adSpace.getCreative()[0];
		String[] impUrls = creative.getImpression();
		if (impUrls != null) {
			String[] newImpUrls = new String[impUrls.length + 1];
			for (int k = 0; k < impUrls.length; k++) {
				newImpUrls[k + 1] = impUrls[k];
			}

			newImpUrls[0] = impUrl;

			// String eventValue = creative.getEvent()[0].getEventValue();
			// if (StringUtils.hasText(eventValue)) {
			// //添加targeturl
			// String convertTarget =
			// convertTargetUrlHandler.getConvertTarget(adContext, eventValue);
			// if(StringUtils.hasText(convertTarget)){
			// String[] impUrls2 = new String[newImpUrls.length + 1];
			// for (int k = 0; k < newImpUrls.length; k++) {
			// impUrls2[k] = newImpUrls[k];
			// }
			// impUrls2[impUrls.length] = convertTarget;
			// newImpUrls=impUrls2;
			// }
			// }

			creative.setImpression(newImpUrls);
		} else {

			String[] newImpUrls = null;

			// String eventValue = creative.getEvent()[0].getEventValue();
			// if (StringUtils.hasText(eventValue)) {
			// //添加targeturl
			// String convertTarget =
			// convertTargetUrlHandler.getConvertTarget(adContext, eventValue);
			// newImpUrls = new String[]{impUrl,convertTarget};
			// }else{
			// newImpUrls = new String[]{impUrl};
			// }
			//
			// 下面一行代码 和 上面注释 选择其一
			newImpUrls = new String[] { impUrl };

			creative.setImpression(newImpUrls);
		}

		// String clickUrlFormat = serverAddress +
		// "click.html?version=%s&aid=%s&sid=%s&cid=%s&mid=%s&guid=%s";
		// String clickUrl = String.format(clickUrlFormat,
		// adRequest.getVersion(), adContext.getAdSpaceId(),
		// adContext.getStrategyId(), adContext.getCreativeId(),
		// adContext.getMaterialId(), adContext.getGuid())
		// +
		// "&lon=%%LON%%&lat=%%LAT%%&downx=%%DOWNX%%&downy=%%DOWNY%%&upx=%%UPX%%&upy=%%UPY%%&target=";

		String targetUrl = null;
		String eventValue = creative.getEvent()[0].getEventValue();
		if (StringUtils.hasText(eventValue)) {
			try {
				targetUrl = URLEncoder.encode(convertTargetUrlHandler.getConvertTarget(adContext, eventValue), "UTF-8");
			} catch (UnsupportedEncodingException e) {
				LOGGER.error("url encode target url error {}", e.getMessage());
			}
		}

		// 添加扩展url
		String extend = convertTargetUrlHandler.convertExtendClickUrl(adContext);

		String clickUrl = trackingService.buildClickUrl2(adRequest.getVersion() + "",
				NumberUtils.toInt(adRequest.getChannelID(), 1), 1, adContext.getAdSpaceId(), adContext.getStrategyId(),
				adContext.getCreativeId(), adContext.getMaterialId(), adContext.getGuid(), targetUrl, extend, adContext.getOs());
		clickUrl += CLICK_DEFINITION;
		creative.getEvent()[0].setEventValue(clickUrl);
		return adSpace;
	}

	// /**
	// * 填充TrackCommand
	// *
	// *
	// 注：STSAdRequestAction，STSAdProcessor，ImpressionHandler，ImpressionMatchService
	// 中为同一份,有点不同。代码冗余。
	// * @param bidContext
	// * @param command
	// * @return
	// */
	// protected TrackCommand populateCommand(AdContext bidContext,TrackCommand
	// command){
	// if(command==null){
	// command = new TrackCommand();
	// }
	//// os={os}&mac={mac}&mac_md5={mac_md5}&idfa={idfa}
	//// &idfa_md5={idfa_md5}&imei={imei}&imei_md5={imei_md5}&
	//// androidid={androidid}&androidid_md5={androidid_md5}
	//
	//// &aid={aid}&sid={sid}&cid={cid}&mid={mid}&guid={guid}
	// command.setOs(bidContext.getOs());
	// command.setMac("");
	// command.setMac_md5(bidContext.getMac());
	// command.setIdfa("");
	// command.setIdfa_md5(bidContext.getIdfa());
	// command.setImei("");
	// command.setImei_md5(bidContext.getImei());
	// command.setAndroidid("");
	// command.setAndroidid_md5(bidContext.getAndroidId());
	// command.setAid(bidContext.getAdSpaceId());
	// command.setSid(bidContext.getStrategyId());
	// command.setCid(bidContext.getCreativeId());
	// command.setMid(bidContext.getMaterialId());
	// command.setGuid(bidContext.getGuid());
	// return command;
	// }

	// /**
	// * 得到转换的targetUrl
	// *
	// * @param bidContext
	// * @param entry
	// * @return
	// */
	// private String getConvertTarget(AdContext bidContext, String eventValue)
	// {
	// String targetUrl ="";
	// if (StringUtils.hasText(eventValue)) {
	// //宏替换。
	// TrackCommand command = new TrackCommand();
	// populateCommand(bidContext, command);
	// String targetUrl2 = eventValue;
	// String targetUrlPrefix = HttpUtils.getUrlDomain(targetUrl2);
	//
	// //根据前缀得到合适的转换器进行转换。
	// IConvertTargetUrl convert = (IConvertTargetUrl)
	// targetUrlConvert.get(targetUrlPrefix);
	// if(convert!=null){
	// targetUrl2 = convert.convertTargetUrl(targetUrl2, command);
	// targetUrl = targetUrl2;
	// }else{
	// targetUrl = targetUrl2;
	// }
	// }
	// return targetUrl;
	// }

	/**
	 * 匹配默认策略
	 *
	 * @param adSpace
	 * @param adContext
	 * @return
	 */
	protected void matchStrategy(AdSpace adSpace, AdContext adContext) {

		// 获取广告位对应的策略集合
		List<Strategy> strategyList = adSpace.getStrategys();

		Iterator<Strategy> it = strategyList.iterator();

		while (it.hasNext()) {

			Strategy strategy = it.next();
			// 判断策略当前时间是否在策略时间内
			long time = System.currentTimeMillis();
			long startTime = strategy.getStartTime().getTime();
			long endTime = strategy.getEndTime().getTime();

			double ratio = adService.getRatio(strategy.getCampaign().getId());

			if (adSpace.getChargePrice() != null && adSpace.getChargePrice()
					.subtract(strategy.getUnitPrice().multiply(new BigDecimal(ratio))).doubleValue() > 0) {
				if (debugMode) {
					LOGGER.info("adSpace price high over strategy price", strategy.getId());
				}
				MonitorHandler.monitorStrategy(configRepository.fetchMonitoredStrategy(),
						configRepository.fetchDebugMode(), strategy.getClass().getName(),
						String.valueOf(strategy.getId()), "adSpace price high over strategy price");
				it.remove();
				continue;
			}

			if (time < startTime || time > endTime) {
				if (debugMode) {
					LOGGER.info("strategy ({}) is not in time range", strategy.getId());
				}
				MonitorHandler.monitorStrategy(configRepository.fetchMonitoredStrategy(),
						configRepository.fetchDebugMode(), strategy.getClass().getName(),
						String.valueOf(strategy.getId()), "is not in time range");
				it.remove();
				continue;
			}

			if (!matcherChain.match(strategy, adContext)) {
				if (debugMode) {
					LOGGER.info("strategy ({}) target is not matched", strategy.getId());
				}

				it.remove();
				continue;
			}

			if (!adService.whetherHaveBalance(strategy)) {
				if (debugMode) {
					LOGGER.info("strategy ({}) have no balance", strategy.getId());
				}
				MonitorHandler.monitorStrategy(configRepository.fetchMonitoredStrategy(),
						configRepository.fetchDebugMode(), strategy.getClass().getName(),
						String.valueOf(strategy.getId()), "have no balance");
				it.remove();
				continue;
			}

		}
		// 过滤优先级
		strategyList = priorityFilter(strategyList);

		if (strategyList.size() == 0) {
			// 选择默认策略
		} else if (strategyList.size() == 1) {
			adSpace.setMatchStrategy(strategyList.get(0));
		} else {
			adSpace.setMatchStrategy(strategyList.get(new Random().nextInt(strategyList.size())));
		}
	}

	/**
	 * 匹配素材
	 * 
	 * @param adSpace
	 */
	protected void matchMaterial(AdSpace adSpace, AdRequestCommand command) {

		AdMaterial adMaterial = chooseMaterial(adSpace.getMatchCreative().getAdMaterials(), command, adSpace);
		adSpace.setMatchAdMaterial(adMaterial);

	}

	private List<Strategy> priorityFilter(List<Strategy> strategyList) {

		if (strategyList.size() <= 1) {
			return strategyList;
		}

		int minPriority = 0;
		List<Strategy> filterStrategyList = new ArrayList<>();
		for (int i = 0; i < strategyList.size(); i++) {

			Strategy strategy = strategyList.get(i);
			int priority = strategy.getPriority();
			if (i == 0) {
				minPriority = priority;
				filterStrategyList.add(strategy);
			} else {
				if (priority < minPriority) {
					filterStrategyList.clear();
					filterStrategyList.add(strategy);
					minPriority = priority;
				} else if (priority == minPriority) {
					filterStrategyList.add(strategy);
				}
			}

		}
		return filterStrategyList;
	}

	/**
	 * 计算最符合尺寸的素材
	 */
	private AdMaterial chooseMaterial(List<AdMaterial> adMaterials, AdRequestCommand aCommand, AdSpace adSpace) {

		List<AdMaterial> fitMaterials = null;

		if (aCommand.getaWidth() > 0) {
			int width = aCommand.getaWidth();
			int height = aCommand.getaHeight();

			fitMaterials = calMaterialSize(adMaterials, width, width, height);

		} else {
			Ratio ratio = adSpace.getRatio();
			String ratioValue = ratio.getValue();
			String[] ratioSize = ratioValue.split(":");
			double width = Double.parseDouble(ratioSize[0]);
			double height = Double.parseDouble(ratioSize[1]);

			if (aCommand.getScreenWidth() > 0) {
				int screenWidth = aCommand.getScreenWidth();
				fitMaterials = calMaterialSize(adMaterials, screenWidth, width, height);
			} else {

				fitMaterials = adMaterials;

				Collections.sort(fitMaterials, new Comparator<AdMaterial>() {
					@Override
					public int compare(AdMaterial o1, AdMaterial o2) {
						return o1.getAdSize().getWidth() - o2.getAdSize().getWidth();
					}
				});

				if (!CollectionUtils.isEmpty(fitMaterials) && fitMaterials.size() > 0) {
					return fitMaterials.get(0);
				}

			}

		}

		if (fitMaterials.size() == 0) {
			return null;
		}

		return fitMaterials.get(new Random().nextInt(fitMaterials.size()));

	}

	private List<AdMaterial> calMaterialSize(List<AdMaterial> adMaterials, int thresholdWidth, double width,
                                             double height) {

		double min = 0.0d;
		boolean init = true;
		int minDivide = 0;
		List<AdMaterial> fitMaterials = new ArrayList<>();

		// 取宽度小于屏幕宽度的素材
		for (AdMaterial adMaterial : adMaterials) {
			if (adMaterial.getAdSize() == null)
				continue;
			if (adMaterial.getAdSize().getWidth() <= thresholdWidth) {
				double d = adMaterial.computeD(width, height);
				int divide = thresholdWidth - adMaterial.getAdSize().getWidth();
				d = Double.parseDouble(df.format(d));

				if (init) {
					fitMaterials.add(adMaterial);
					min = d;
					minDivide = divide;
					init = false;
				} else {
					if (d < min) {
						fitMaterials.clear();
						fitMaterials.add(adMaterial);
						min = d;
						minDivide = divide;
					} else if (d == min) {
						if (divide == minDivide) {
							fitMaterials.add(adMaterial);
						}

						if (divide < minDivide) {
							fitMaterials.clear();
							fitMaterials.add(adMaterial);
							minDivide = divide;
						}
					}
				}
			}
		}

		return fitMaterials;
	}

	/**
	 * 生成并存储GUID对象
	 * @param adContext
	 * @param device
	 */
	private void generateGuid(AdContext adContext, STSAdRequest.Device device, AdSpace adSpace, Strategy s) {

		// 生成guid
		GuidValueObject guidVo = new GuidValueObject();
		guidVo.setAid(adContext.getAdSpaceId());
		guidVo.setSid(adContext.getStrategyId());
		guidVo.setCid(adContext.getCreativeId());
		guidVo.setCampaignId(adContext.getCampaignId());
		guidVo.setMid(adContext.getMaterialId());
		guidVo.setMuniqueId(adContext.getMuniqueId());
		guidVo.setMuniqueType(adContext.getMuniqueType());
		guidVo.setBrand(adContext.getBrand());
		guidVo.setOs(adContext.getOs());
		guidVo.setPlatform(adContext.getModel());
		guidVo.setDeviceSize(device.getDeviceSize());
		guidVo.setNetwork(adContext.getConnType());
		guidVo.setOperator(adContext.getMob_ops());
		guidVo.setIp(adContext.getIp());
		guidVo.setLongitude(adContext.getLongitude());
		guidVo.setLatitude(adContext.getLatitude());

		guidVo.setMac(adContext.getMac());
		guidVo.setMac1(adContext.getMac1());
		guidVo.setImei(adContext.getImei());
		guidVo.setAnid(adContext.getAndroidId());
		guidVo.setAaid(adContext.getAaid());
		guidVo.setIdfa(adContext.getIdfa());
		guidVo.setUdid(adContext.getUdid());
		guidVo.setDuid(adContext.getDuid());

		guidVo.setFid(adContext.getAdFormat());
		guidVo.setTemplateId(adContext.getTemplateId());

		adContext.setGuid(adService.storeGuid(guidVo, 1, adSpace, s));
	}
}
